﻿using Microsoft.AspNetCore.Mvc;
using Minio;
using Minio.DataModel.Args;
using Newtonsoft.Json;

namespace I_FileUpload.Controllers
{
    /// <summary>
    /// 文件管理
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class FileController : ControllerBase
    {
        private readonly IMinioClient _minioClient;
        private string bucketName = "sheepimages";

        public FileController()
        {
            _minioClient = new MinioClient()
                            .WithEndpoint("47.95.178.186:9000")       // MinIO 服务器的端点地址为 "10.31.60.18:9000"
                            .WithCredentials("minioadmin", "minioadmin")   // MinIO 服务器连接所需的凭据，包括用户名和密码
                            .WithSSL(false)                         // MinIO 服务器是否启用 SSL 加密通信
                            .Build();                               // MinIO 服务器配置完成，构建 MinioClient 实例
        }

        /// <summary>
        /// 列出所有桶
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> ListBuckets()
        {
            // ListBucketsAsync() 来获取 MinIO 服务器上的所有存储桶（buckets
            // ConfigureAwait(false) 的作用是防止异步操作回到调用线程上继续执行，以避免死锁。
            var buckets = await _minioClient.ListBucketsAsync().ConfigureAwait(false);
            return Ok(buckets);
        }

        /// <summary>
        /// 上传单个文件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> UploadFile(IFormFile file)
        {

            // 检查桶是否存在
            var beArgs = new BucketExistsArgs()
                     .WithBucket(bucketName);
            bool found = await _minioClient.BucketExistsAsync(beArgs);
            // 如果桶不存在，则创建桶
            if (!found)
            {
                // MakeBucketArgs 是用于配置创建存储桶操作的参数对象
                var mbArgs = new MakeBucketArgs().WithBucket(bucketName);
                await _minioClient.MakeBucketAsync(mbArgs).ConfigureAwait(false);
            }

            // 获取文件名
            var objectName = file.FileName;
            
            // 使用流数据上传文件
            // 注意：MinIO API 要求 Content-Type 和 ObjectSize 参数 
            var putArgs = new PutObjectArgs() // PutObjectArgs 对象，用于配置上传对象的参数
                .WithBucket(bucketName)                  // WithBucket 方法设置要上传到的存储桶的名称为 bucketName
                .WithObject(objectName)                  // WithObject 方法设置要上传的对象的名称为 objectName
                .WithStreamData(file.OpenReadStream())   // WithStreamData 设置上传对象数据流 file.OpenReadStream()，file文件，OpenReadStream()返回文件数据流
                .WithContentType(file.ContentType)       // WithContentType 方法设置要上传的对象的内容类型为 file.ContentType，即文件的 MIME 类型
                .WithObjectSize(file.Length);            // WithObjectSize 方法设置要上传的对象的大小为 file.Length，即文件的字节大小
            await _minioClient.PutObjectAsync(putArgs).ConfigureAwait(false);   // PutObjectAsync 用于将对象上传到 MinIO 对象存储服务中

            // 在服务器端生成一个预签名 URL
            // 客户端可以使用该 URL 来直接下载 MinIO 存储中的特定对象
            // 并将其作为 JSON 格式的响应返回给客户端
            var getArgs = new PresignedGetObjectArgs()  //创建 PresignedGetObjectArgs 对象
                .WithBucket(bucketName)     // 设置获取对象的存储桶名称
                .WithObject(objectName)     // 设置对象名称
                .WithExpiry(7200);          // 设置预签名 URL 的有效期（在这里是7200秒）(链式调用)
            var url = await _minioClient.PresignedGetObjectAsync(getArgs).ConfigureAwait(false);
            return Ok(JsonConvert.SerializeObject(url));
        }

        /// <summary>
        /// 上传多个文件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> UploadFiles(IFormFileCollection formFiles)
        {

            // 检查桶是否存在
            var beArgs = new BucketExistsArgs()
                     .WithBucket(bucketName);
            bool found = await _minioClient.BucketExistsAsync(beArgs);
            // 如果桶不存在，则创建桶
            if (!found)
            {
                var mbArgs = new MakeBucketArgs()
                    .WithBucket(bucketName);
                await _minioClient.MakeBucketAsync(mbArgs).ConfigureAwait(false);
            }
            var formFileList = new List<string>();
            foreach (var formFile in formFiles)
            {

                using var stream = formFile.OpenReadStream();
                // 获取文件名
                var objectName = formFile.FileName;
                formFileList.Add(objectName);
                // 使用流数据上传文件
                // 注意：MinIO API 要求 Content-Type 和 ObjectSize 参数 
                var putArgs = new PutObjectArgs()
                    .WithBucket(bucketName)
                    .WithObject(objectName)
                    .WithStreamData(stream)
                    .WithContentType(formFile.ContentType)
                    .WithObjectSize(stream.Length);
                await _minioClient.PutObjectAsync(putArgs).ConfigureAwait(false);
            }
            return Ok(formFileList);
        }
    }
}
